#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import csv
import json
import os
import sys
import urllib.request
from typing import Any, Dict, List, Tuple


OPENAPI_URL = "http://43.153.150.144:8000/openapi.json"

# 关键词规则
TAG_KEYWORDS = {"connector", "connectors", "连接器", "交易所", "exchange"}
PATH_KEYWORDS = {"/connector", "/connectors", "/exchange"}
TEXT_KEYWORDS = {"connector", "connectors", "连接器", "交易所", "exchange"}

EXPORT_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "exports")
JSON_PATH = os.path.join(EXPORT_DIR, "connectors.json")
CSV_PATH = os.path.join(EXPORT_DIR, "connectors.csv")


def fetch_openapi(url: str) -> Dict[str, Any]:
    req = urllib.request.Request(url)
    with urllib.request.urlopen(req, timeout=20) as resp:
        charset = resp.headers.get_content_charset() or "utf-8"
        data = resp.read().decode(charset, errors="replace")
        return json.loads(data)


def safe_get(d: Dict[str, Any], key: str, default: Any = None) -> Any:
    return d.get(key, default) if isinstance(d, dict) else default


def extract_schema_type(schema: Dict[str, Any]) -> str:
    if not isinstance(schema, dict):
        return ""
    t = schema.get("type")
    if not t:
        # try resolve $ref simple tail
        ref = schema.get("$ref")
        if isinstance(ref, str):
            return ref.rsplit("/", 1)[-1]
    return str(t or "")


def summarize_request_body(request_body: Dict[str, Any]) -> Dict[str, Any]:
    if not isinstance(request_body, dict):
        return {}
    content = safe_get(request_body, "content", {})
    content_types: List[str] = list(content.keys())
    # Grab first schema type as hint
    first_schema_type = ""
    for media in content_types:
        schema = safe_get(content.get(media, {}), "schema", {})
        first_schema_type = extract_schema_type(schema)
        if first_schema_type:
            break
    return {
        "required": bool(request_body.get("required", False)),
        "contentTypes": content_types,
        "schemaType": first_schema_type,
    }


def summarize_responses(responses: Dict[str, Any]) -> Dict[str, Any]:
    if not isinstance(responses, dict):
        return {}
    out: Dict[str, Any] = {}
    for code in ("200", "201", "default"):
        if code in responses:
            resp = responses[code]
            content = safe_get(resp, "content", {})
            # pick first content type
            schema_type = ""
            for media in content.keys():
                schema = safe_get(content.get(media, {}), "schema", {})
                schema_type = extract_schema_type(schema)
                if schema_type:
                    break
            out[code] = {
                "description": resp.get("description", ""),
                "schemaType": schema_type,
            }
    return out


def normalize_param(param: Dict[str, Any]) -> Dict[str, Any]:
    name = param.get("name", "")
    location = param.get("in", "")
    required = bool(param.get("required", False))
    schema = safe_get(param, "schema", {})
    schema_type = extract_schema_type(schema)
    return {
        "name": name,
        "in": location,
        "required": required,
        "type": schema_type,
    }


def should_keep(path: str, method_obj: Dict[str, Any]) -> bool:
    # tags rule
    tags: List[str] = [str(t).lower() for t in method_obj.get("tags", [])]
    if any(t in TAG_KEYWORDS for t in tags):
        return True
    # path rule
    lower_path = path.lower()
    if any(k in lower_path for k in PATH_KEYWORDS):
        return True
    # text rule
    summary = str(method_obj.get("summary", "")).lower()
    description = str(method_obj.get("description", "")).lower()
    combined = f"{summary} {description}"
    if any(k in combined for k in TEXT_KEYWORDS):
        return True
    return False


def collect_operations(spec: Dict[str, Any]) -> List[Dict[str, Any]]:
    paths = safe_get(spec, "paths", {})
    if not isinstance(paths, dict):
        return []
    results: List[Dict[str, Any]] = []
    http_methods = {"get", "post", "put", "delete", "patch", "options", "head"}
    for path, methods in paths.items():
        if not isinstance(methods, dict):
            continue
        for method, op in methods.items():
            if method.lower() not in http_methods or not isinstance(op, dict):
                continue
            if not should_keep(path, op):
                continue
            parameters = [normalize_param(p) for p in op.get("parameters", []) if isinstance(p, dict)]
            request_body = summarize_request_body(op.get("requestBody", {}))
            responses = summarize_responses(op.get("responses", {}))
            record = {
                "method": method.upper(),
                "path": path,
                "summary": op.get("summary", "") or op.get("description", ""),
                "tags": op.get("tags", []),
                "operationId": op.get("operationId", ""),
                "parameters": parameters,
                "requestBody": request_body,
                "responses": responses,
            }
            results.append(record)
    return results


def ensure_export_dir() -> None:
    os.makedirs(EXPORT_DIR, exist_ok=True)


def export_json(rows: List[Dict[str, Any]], path: str) -> None:
    with open(path, "w", encoding="utf-8") as f:
        json.dump(rows, f, ensure_ascii=False, indent=2)


def export_csv(rows: List[Dict[str, Any]], path: str) -> None:
    fieldnames = [
        "method",
        "path",
        "summary",
        "tags",
        "operationId",
        "parameters",
        "requestBody",
        "responses",
    ]
    with open(path, "w", encoding="utf-8", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        for r in rows:
            writer.writerow({
                "method": r.get("method", ""),
                "path": r.get("path", ""),
                "summary": r.get("summary", ""),
                "tags": ",".join(r.get("tags", [])),
                "operationId": r.get("operationId", ""),
                "parameters": json.dumps(r.get("parameters", []), ensure_ascii=False),
                "requestBody": json.dumps(r.get("requestBody", {}), ensure_ascii=False),
                "responses": json.dumps(r.get("responses", {}), ensure_ascii=False),
            })


def main() -> int:
    try:
        spec = fetch_openapi(OPENAPI_URL)
    except Exception as e:
        print(f"[ERROR] Failed to fetch openapi.json from {OPENAPI_URL}: {e}")
        return 2

    rows = collect_operations(spec)
    ensure_export_dir()
    export_json(rows, JSON_PATH)
    export_csv(rows, CSV_PATH)

    print(f"Total connector-related operations: {len(rows)}")
    for idx, r in enumerate(rows[:5]):
        print(f"[{idx+1}] {r.get('method')} {r.get('path')} - {r.get('summary')}")
    print(f"JSON -> {JSON_PATH}")
    print(f"CSV  -> {CSV_PATH}")
    return 0


if __name__ == "__main__":
    sys.exit(main())


